package com.uit.httt04.duongvanlam.facedetection.extend;

import java.io.IOException;
import java.util.List;

import com.uit.httt04.duongvanlam.facedetection.utils.ClassLogger;
import com.uit.httt04.duongvanlam.facedetection.utils.Configuration;

import android.app.Activity;
import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback
{
	private String TAG = "CameraPreview";
	//
	private Camera camera;
	private int cameraID;
	private int cameraWidth;
	private int cameraHeight;
	private int cameraFormat;
	private int cameraRealRotation;
	private boolean cameraPreview;
	private boolean cameraStartNow;
	private byte[] cameraData;
	//
	private SurfaceHolder surfaceHolder;
	// For processing frame on Object
	private Object processFrame;
	// Activity
	private Activity activity;

	//
	public int getCameraID()
	{
		return this.cameraID;
	}

	public int getCameraRealRotation()
	{
		return cameraRealRotation;
	}

	private void setCameraRealRotation(int cameraRealRotation)
	{
		this.cameraRealRotation = cameraRealRotation;
	}

	public CameraPreview(Context context)
	{
		super(context);
	}

	public CameraPreview(Context context, AttributeSet attributeSet)
	{
		super(context, attributeSet);
	}

	public CameraPreview(Context context, AttributeSet attributeSet, int style)
	{
		super(context, attributeSet, style);
	}

	public void Load(Activity activity, Object ProcessFrame, boolean StartNow, int CameraHeight, int CameraWidth, boolean FrontFacing) throws Exception
	{
		if (ProcessFrame instanceof PreviewCallback == false)
		{
			throw new Exception("class: CameraPreview, method: Load, Parameter 'processFrame': isn't instanceof PreviewCallback, please implement PreviewCallback");
		}
		this.activity = activity;
		this.cameraStartNow = StartNow;
		this.processFrame = ProcessFrame;
		this.cameraHeight = CameraHeight;
		this.cameraWidth = CameraWidth;
		this.cameraFormat = ImageFormat.NV21;
		int totalBytes = (int) (this.cameraHeight * this.cameraWidth * Configuration.cameraBytePerPixel);
		this.cameraData = new byte[totalBytes];

		try
		{
			this.initControls(FrontFacing);
		}
		catch (Exception e)
		{
			throw e;
		}
	}

	private void initControls(boolean frontFace) throws Exception
	{
		int cameraId = this.findCamera(frontFace);
		if (cameraId == -1)
			throw new Exception("Camera dont found");
		//
		this.cameraID = cameraId;
		//
		surfaceHolder = getHolder();
		surfaceHolder.addCallback(this);
	}

	private int findCamera(boolean frontFace)
	{
		int cameraID = -1;
		int numberOfCameras = Camera.getNumberOfCameras();
		for (int i = 0; i < numberOfCameras; i++)
		{
			CameraInfo info = new CameraInfo();
			Camera.getCameraInfo(i, info);
			if (frontFace == true)
			{
				if (info.facing == CameraInfo.CAMERA_FACING_FRONT)
				{
					cameraID = i;
					break;
				}
			}
			else
			{
				if (info.facing == CameraInfo.CAMERA_FACING_BACK)
				{
					cameraID = i;
					break;
				}
			}
		}
		return cameraID;
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
	{
		Log.d(TAG, "surfaceChanged");
	}

	@Override
	public void surfaceCreated(SurfaceHolder surfaceHolder)
	{
		Log.d(TAG, "surfaceCreated");
		try
		{
			this.setKeepScreenOn(true);
			//
			this.camera = Camera.open(this.cameraID);
			//
			// <-- get camera's information
			//
			List<Size> listSize = this.camera.getParameters().getSupportedPictureSizes();
			Log.d(TAG, "SupportedPictureSizes");
			for (int i = 0; i < listSize.size(); i++)
			{
				Log.d(TAG, "height:" + listSize.get(i).height + ", width:" + listSize.get(i).width);
			}
			//
			// get camera's information -->
			//
			Camera.Parameters parameters = this.camera.getParameters();
			parameters.setPreviewSize(this.cameraWidth, this.cameraHeight);
			parameters.setPreviewFormat(this.cameraFormat);
			this.setCameraDisplayOrientation(this.activity, this.cameraID, this.camera);
			this.camera.setParameters(parameters);
			this.camera.setPreviewDisplay(this.surfaceHolder);
			if (this.cameraStartNow == true)
			{
				this.startPreview();
			}
		}
		catch (IOException iOException)
		{
			ClassLogger.Write(this.getClass().getName(), "surfaceCreated", iOException.toString());
			iOException.printStackTrace();
		}
		catch (Exception exception)
		{
			ClassLogger.Write(this.getClass().getName(), "surfaceCreated", exception.toString());
			exception.printStackTrace();
		}
	}

	public void getFrame() throws Exception
	{
		try
		{
			if (this.camera != null)
				this.camera.addCallbackBuffer(this.cameraData);
		}
		catch (Exception e)
		{
			throw e;
		}
	}

	public void startPreview() throws Exception
	{
		try
		{
			if (this.camera != null)
				if (this.cameraPreview == false)
				{
					this.camera.setPreviewCallbackWithBuffer((PreviewCallback) this.processFrame);
					this.camera.startPreview();
					this.getFrame();
					this.cameraPreview = true;
				}
		}
		catch (Exception e)
		{
			throw e;
		}
	}

	public void stopPreview() throws Exception
	{
		try
		{
			if (this.camera != null)
				if (this.cameraPreview == true)
				{
					this.camera.stopPreview();
					this.cameraPreview = false;
				}
		}
		catch (Exception e)
		{
			throw e;
		}
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder)
	{
		try
		{
			Log.d(TAG, "surfaceDestroyed");
			if (this.camera != null)
			{
				this.camera.setPreviewCallback(null);
				this.camera.stopPreview();
				this.camera.release();
				this.camera = null;
			}
		}
		catch (Exception e)
		{
			ClassLogger.Write(this.getClass().getName(), "surfaceDestroyed", e.toString());
			e.printStackTrace();
		}
	}

	public void setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) throws Exception
	{
		try
		{
			CameraInfo info = new android.hardware.Camera.CameraInfo();
			Camera.getCameraInfo(cameraId, info);
			int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
			int degrees = 0;
			switch (rotation)
			{
				case Surface.ROTATION_0:
					degrees = 0;
					break;
				case Surface.ROTATION_90:
					degrees = 90;
					break;
				case Surface.ROTATION_180:
					degrees = 180;
					break;
				case Surface.ROTATION_270:
					degrees = 270;
					break;
			}

			int result;
			if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
			{
				result = (info.orientation + degrees) % 360;
				result = (360 - result) % 360; // compensate the mirror
			}
			else
			{ // back-facing
				result = (info.orientation - degrees + 360) % 360;
			}

			this.setCameraRealRotation(result);
			camera.setDisplayOrientation(this.getCameraRealRotation());
		}
		catch (Exception e)
		{
			throw e;
		}
	}
}
